#!/usr/bin/env python2

import os
import sys
import errno
import getopt
import copy
import time

import warnings
warnings.filterwarnings("ignore")

import MySQLdb
from mysql import _MySQL

def _derror(str1, ttyonly = False):
    if (os.isatty(sys.stderr.fileno()) or ttyonly):
        sys.stderr.write("\x1b[1;31m%s\x1b[0m\n" % (str1))
    else:
        sys.stderr.write("[%s %s] ERROR:%s\n" % (int(time.time()), time.strftime('%F %T'), str1))

host = 'localhost'
port = 3306
user = 'root'
passwd = 'mdsmds'
db = 'group_db'

class Group(object):
    def __init__(self):
        self.mysql = _MySQL(host, port, user, passwd, db)
        self.update_all()

    def update_groups(self): 
        groups = {}
        sql = 'select * from groups;'
        _groups = self.mysql.query(sql)
        if _groups is not None:
            for g in _groups:
                groups[g['name']] = g['id']
        self.groups = groups

    def update_vols(self):
        vols = {}
        sql = 'select * from vols;'
        _vols = self.mysql.query(sql)
        if _vols is not None:
            for v in _vols:
                name = v['name']
                vols[name] = {} 
                vols[name]['id'] = v['id']
                vols[name]['group_id'] = v['group_id']
        self.vols = vols

    def update_snaps(self):
        snaps = {}
        sql = 'select * from snaps;'
        _snaps = self.mysql.query(sql)
        if _snaps is not None:
            for s in _snaps:
                name = s['name']
                snaps[name] = {}
                snaps[name]['id'] = s['id']
                snaps[name]['group_id'] = s['group_id']
        self.snaps = snaps

    def update_snap_vols(self):
        snap_vols = {}
        sql = 'select * from snap_vols;'
        _snap_vols = self.mysql.query(sql)
        if _snap_vols is not None:
            for sv in _snap_vols:
                _id = sv['id']
                snap_vols[_id] = {}
                snap_vols[_id]['snap_id'] = sv['snap_id']
                snap_vols[_id]['vol_id'] = sv['vol_id']
                snap_vols[_id]['group_id'] = sv['group_id']
        self.snap_vols = snap_vols

    def update_all(self):
        self.update_groups()
        self.update_vols()
        self.update_snaps()
        self.update_snap_vols()

    def create(self, g_name='', vols=[]): 
        if g_name == '' or len(vols) < 2:
            _derror("input err need group name and volumes")
            exit(errno.EINVAL)

        # check group exist
        if g_name in self.groups:
            _derror("(%s) already exists"%g_name)
            exit(errno.EEXIST)

        # check vol exist
        for v in vols: 
            if v in self.vols:
                g_id = self.vols[v]['group_id']
                gname = ''
                for g, _id in self.groups.iteritems():
                    if _id == g_id:
                        gname = g
                _derror("%s already in group (%s)"%(v, gname))
                exit(errno.EEXIST)

        sql = 'INSERT INTO groups(name) values("%s");' %g_name
        ret = self.mysql.execute(sql)
        if ret != 1:
            _derror("insert group err (%s)" %(g_name))
            exit(errno.EPERM)

        self.update_groups()
        g_id = self.groups[g_name]

        sql = 'INSERT INTO vols(name, group_id) values(%s, %s);'
        params = [(v, g_id) for v in vols]
        ret = self.mysql.executemany(sql, params) 
        self.update_vols()
        if ret != len(vols):
            _derror("insert vols err (%s)" %(','.join(vols)))
            rm_vols = [v for v in vols if v in self.vols]
            self.remove_vols(rm_vols)
            self.remove_group(g_name)
            exit(errno.EPERM)

        print "create %s success" %g_name

    def remove_group(self, g_name):
        sql = 'DELETE FROM groups where name="%s";' %g_name
        ret = self.mysql.execute(sql)
        if ret != 1:
            _derror("delete group (%s) failed" % g_name)

    def remove_vols(self, vols):
        sql = 'DELETE FROM vols where name=%s;'
        ret = self.mysql.executemany(sql, vols)
        if ret != len(vols):
            _derror("delete vols (%s) failed" % (','.join(vols)))

    def remove_vols_by_gid(self, gid):
        sql = 'DELETE FROM vols where group_id=%s;'
        ret = self.mysql.execute(sql, gid)
        if ret == 0:
            _derror("delete vols by gid (%d) failed" % (gid))

    def remove(self, g_name): 
        if g_name not in self.groups:
            return
        g_id = self.groups[g_name]
        self.remove_group(g_name)
        self.remove_vols_by_gid(g_id)

    # snap
    def snap_create(self, name):
        group,snap = name.split('@')
        if not group or not snap:
            print "input error"
            exit(errno.EINVAL)
        
        if group not in self.groups:
            _derror(group + " is not a group")
            exit(errno.ENOENT)

        gid = self.groups[group]
        if snap in self.snaps \
            and self.snaps[snap]['group_id'] == gid:
            _derror(snap + " is already exist")
            exit(errno.EEXIST)

        '''
         add in lich at here 
        '''

        sql = 'INSERT INTO snaps(name, group_id) values(%s, %s);'
        ret = self.mysql.execute(sql, (snap, gid))
        if ret == 0:
            _derror("insert (%s) to snaps failed" % (snap))

        self.update_snaps()
        sid = self.snaps[snap]['id']

        vol_ids = [info['id'] for v,info in self.vols.iteritems() if info['group_id'] == gid]

        sql = 'INSERT INTO snap_vols(snap_id, group_id, vol_id) values(%s, %s, %s);'
        params = [(sid, gid, vid) for vid in vol_ids]
        ret = self.mysql.executemany(sql, params)
        if ret != len(params):
            _derror("insert snap_vols failed")

    def snap_remove(self, name):
        group,snap = name.split('@')
        if not group or not snap:
            print "input error"
            exit(errno.EINVAL)
        
        if group not in self.groups:
            _derror(group + " is not a group")
            exit(errno.ENOENT)

        gid = self.groups[group]

        if snap not in self.snaps \
            or self.snaps[snap]['group_id'] != self.groups[group]:
            _derror(snap + " not exist")
            exit(errno.ENOENT)
        sid = self.snaps[snap]['id']

        '''
        delete lich at here
        '''
        sql = 'DELETE FROM snaps WHERE id=%s;'
        ret = self.mysql.execute(sql, sid)
        if ret == 0:
            _derror("delete (%s) from snaps  failed" % (snap))

        sql = 'DELETE FROM snap_vols WHERE snap_id=%s AND group_id=%s;'
        ret = self.mysql.execute(sql, (sid, gid))
        if ret == 0:
            _derror("insert snap_vols failed")

    def show(self, v=True):
        if (v):
            print "groups", self.groups
            print "vols", self.vols
            print "snaps", self.snaps
            print "snap_vols ", self.snap_vols

def usage(unhide):
    print ("usage:")
    print (sys.argv[0] + " --create <group name> <volume1 volume2 ...>")
    print (sys.argv[0] + " --remove <group name>")
    print (sys.argv[0] + " --list")
    print (sys.argv[0] + " --snap <create|remove> <group@snap>")
    if unhide:
        print (sys.argv[0] + " --unhide")

def main():
    unhide = False

    try:
        opts, args = getopt.getopt(
                sys.argv[1:], 
                'h', ['help', 'unhide', 'create=', 'remove=', 'list',
                    'snap=']
                )
    except getopt.GetoptError, err:
        print str(err)
        usage(unhide)
        exit(errno.EINVAL)

    newopts = copy.copy(opts)
    for o, a in newopts:
        if o == '--unhide':
            unhide = True
            usage(unhide)
            exit(0)

        elif o in ('-h', '--help'):
            usage(unhide)
            exit(0)

    try:
        group = Group()
    except Exp, e:
        _derror(e.err)
        exit(e.errno)

    for o, a in newopts:
        if o == '--create':
            gname = a
            vols = args
            group.create(gname, vols)

        elif o == '--remove':
            gname = a
            group.remove(gname)

        elif o == '--list':
            group.show()

        elif o == '--snap':
            ext = args[0]
            if a == 'create': 
                group.snap_create(ext)
            elif a == 'remove':
                group.snap_remove(ext)

if __name__ == '__main__':
    if (len(sys.argv) == 1):
        usage(False)
    else:
        main()
